package com.simpou.commons.utils.file;

import com.simpou.commons.utils.string.Strings;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.MissingResourceException;
import java.util.Properties;


/**
 * Operações relacionadas aos arquivos de properties e às propriedades de
 * sistema.
 *
 * @author Jonas Pereira
 * @version 2011-10-19
 * @since 2011-06-23
 */
public final class PropertyHelper {
    /**
     * Extensão padrão dos arquivos de propriedades.
     */
    public static final String FILE_PROPS_EXTENSION = ".properties";

    /**
     * Prefixo do valor padrão para propriedades inseridas. O sufixo é formado
     * pela lista de coringas correspondentes aos parâmetros da propriedade.
     */
    public static final String DEF_VALUE_NOT_SETTED_PREFIX = "<NOT SETTED> ";

    /**
     * Manipulador de propriedades.
     */
    private Properties properties;

    /**
     * Caminho do arquivo de propriedades.
     */
    private final String filePath;

    /**
     * Fluxo de leitura.
     */
    private FileInputStream fi;

    /**
     * Inicializa propriedades.
     *
     * @param filePath Caminha completo do arquivo de properties.
     * @throws java.io.IOException Erro ao acessar arquivo de propriedade.
     */
    public PropertyHelper(final String filePath) throws IOException {
        this.filePath = filePath;
        properties = new Properties();
        load();
    }

    /**
     * <p>getProperty.</p>
     *
     * @param key    Propriedade.
     * @param params Parâmetros a serem substituídos.
     * @return Valor de uma propriedade.
     * @throws java.io.IOException Erro ao acessar arquivo de propriedade.
     */
    public String getProperty(final String key, final String... params)
            throws IOException {
        String prop = properties.getProperty(key);

        if (prop == null) {
            throw new MissingResourceException("", filePath, key);
        }

        if (params.length > 0) {
            prop = Strings.replaceParams(prop, params);
        }

        return prop;
    }

    /**
     * Altera/insere uma propriedade.
     *
     * @param key   Chave.
     * @param value Valor.
     * @throws java.io.IOException Erro ao acessar arquivo de propriedade.
     */
    public void setProperty(final String key, final String value)
            throws IOException {
        properties.setProperty(key, value);
        store();
    }

    /**
     * Fecha o fluxo de leitura.
     *
     * @throws java.io.IOException Erro ao acessar arquivo de propriedade.
     */
    public void close() throws IOException {
        fi.close();
    }

    /**
     * Insere uma propriedade com valor padrão. Este valor é um prefixo
     * (DEF_VALUE_NOT_SETTED_PREFIX) mais a lista de parâmetros na forma:
     * {0} {1} ... {params}.
     *
     * @param key    Chave.
     * @param params Número de parâmetros esperados.
     * @return Valor da propriedade craida.
     * @throws java.io.IOException Erro ao acessar arquivo de propriedade.
     */
    public String setProperty(final String key, final int params)
            throws IOException {
        StringBuilder paramsString = new StringBuilder();

        for (int i = 0; i < params; i++) {
            paramsString.append("{").append(i).append("} ");
        }

        String value = DEF_VALUE_NOT_SETTED_PREFIX + paramsString;
        setProperty(key, value);

        return value;
    }

    /**
     * <p>getSystemProperty.</p>
     *
     * @param property Chave.
     * @return Uma propriedade padrão linguagem Java.
     */
    public static String getSystemProperty(final String property) {
        return System.getProperty(property);
    }

    /**
     * <p>getWorkDir.</p>
     *
     * @return Diretório de trabalho.
     */
    public static String getWorkDir() {
        return getDir("user.dir");
    }

    public static final String MVN_TARGET_TEST_RESOURCE_REL_DIR = "target/test-classes/";

    /**
     * @param relResourceDir Caminho relativo do diretório de recursos do projeto.
     * @return Diretório do projeto. Este diretório pode ser diferente do diretório de trabalho quando os arquivos de
     *         projeto de uma IDE não coincidir com o diretório do projeto em si.
     */
    public static String getPrjDir(final String relResourceDir) {
        final String path = PropertyHelper.class.getClassLoader().getResource(".").getPath();
        final int offset;
        if(File.separator.equals("/")){
            offset = 0;
        }else{
            offset = 1;
        }
        return path.substring(offset, path.length() - relResourceDir.length());
    }

    /**
     * @return Diretório do projeto baseado no padrão do maven. Este diretório pode ser diferente do diretório de
     *         trabalho quando os arquivos de projeto de uma IDE não coincidir com o diretório das classes em si.
     */
    public static String getMvnPrjDir() {
        return getPrjDir(MVN_TARGET_TEST_RESOURCE_REL_DIR);
    }

    /**
     * <p>getWorkDirName.</p>
     *
     * @return Nome do diretório de trabalho.
     */
    public static String getWorkDirName() {
        String workDir = getWorkDir();

        return new File(workDir).getName();
    }

    /**
     * <p>getHomeDir.</p>
     *
     * @return Diretório home do usuário.
     */
    public static String getHomeDir() {
        return getDir("user.home");
    }

    /**
     * <p>getTempDir.</p>
     *
     * @return Diretório temporário do usuário.
     */
    public static String getTempDir() {
        return getDir("java.io.tmpdir");
    }

    /**
     * @param key Propriedade de sistema
     * @return Diretório.
     */
    private static String getDir(final String key) {
        String dir = System.getProperty(key);
        File file = new File(dir);

        return file.getAbsolutePath() + File.separator;
    }

    /**
     * <p>getUser.</p>
     *
     * @return Usuário logado no sistema.
     */
    public static String getUser() {
        return System.getProperty("user.name");
    }

    /**
     * <p>isWindows.</p>
     *
     * @return Se o sistema é windows.
     */
    public static boolean isWindows() {
        return getSystemProperty("os.name").startsWith("Windows");
    }

    /**
     * Carrega propriedades.
     *
     * @throws FileNotFoundException
     * @throws IOException           Erro ao acessar arquivo de propriedade.
     */
    private void load() throws IOException {
        fi = new FileInputStream(filePath);
        properties.load(fi);
    }

    /**
     * Salva modificações no arquivo.
     *
     * @throws IOException Erro ao acessar arquivo de propriedade.
     */
    private void store() throws IOException {
        FileOutputStream fo = new FileOutputStream(filePath);

        try {
            properties.store(fo, null);
        } finally {
            fo.close();
        }
    }
}
